package com.gitee.wsl.android.ui.ext

import android.text.Editable
import android.text.InputFilter
import android.text.InputFilter.LengthFilter
import android.text.InputType
import android.text.Selection
import android.text.TextUtils
import android.text.TextWatcher
import android.text.method.DigitsKeyListener
import android.text.method.HideReturnsTransformationMethod
import android.text.method.PasswordTransformationMethod
import android.widget.EditText
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import timber.log.Timber


object EditTextExt{

    // =======================
    // = Key Listener 快捷处理 =
    // =======================
    // 0123456789
    private val NUMBERS = '0'..'9'

    // abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
    private val LETTERS =   'Z'..'a'

    // 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
    private val NUMBERS_AND_LETTERS = NUMBERS + LETTERS

    /**
     * 设置光标在第一位
     * @param editText [EditText]
     * @param <T>      泛型
     * @return [EditText]
    </T> */
    fun <T : EditText> setSelectionToTop(editText: T): T {
        return setSelection<T>(editText, 0)
    }

    /**
     * 设置光标在最后一位
     * @param editText [EditText]
     * @param <T>      泛型
     * @return [EditText]
    </T> */
    fun <T : EditText> setSelectionToBottom(editText: T): T {
        return setSelection(editText, editText.text.length)
    }

    /**
     * 设置光标位置
     * @param editText [EditText]
     * @param index    光标位置
     * @param <T>      泛型
     * @return [EditText]
    </T> */
    fun <T : EditText> setSelection(
        editText: T,
        index: Int
    ): T {
        if ( index >= 0) {
            // 获取数据长度
            val length = editText.text.toString().length
            // 设置光标
            editText.setSelection(index.coerceAtMost(length))
        }
        return editText
    }

    /**
     * 追加内容
     * @param editText [EditText]
     * @param content  文本内容
     * @param start    开始添加的位置
     * @param isSelect 是否设置光标
     * @param <T>      泛型
     * @return [EditText]
    </T> */
    fun <T : EditText> insert(
        editText: T,
        content: CharSequence,
        start: Int,
        isSelect: Boolean
    ): T {
        if (!TextUtils.isEmpty(content)) {
            try {
                val editable = editText.text
                // 在指定位置 追加内容
                editable.insert(start, content)
                // 设置光标
                if (isSelect) {
                    editText.setSelection(editText.text.toString().length)
                }
            } catch (e: Exception) {
                Timber.d( e, "insert")
            }
        }
        return editText
    }

    // =

    // =
    /**
     * 设置长度限制
     * @param editText  [EditText]
     * @param maxLength 长度限制
     * @param <T>       泛型
     * @return [EditText]
    </T> */
    fun <T : EditText> setMaxLength(
        editText: T,
        maxLength: Int
    ): T {
        if (maxLength > 0) {
            // 设置最大长度限制
            val filters = arrayOf<InputFilter>(LengthFilter(maxLength))
            editText.filters = filters
        }
        return editText
    }

    /**
     * 设置长度限制, 并且设置内容
     * @param editText  [EditText]
     * @param content   文本内容
     * @param maxLength 长度限制
     * @param <T>       泛型
     * @return [EditText]
    </T> */
    fun <T : EditText> setMaxLengthAndText(
        editText: T,
        content: CharSequence,
        maxLength: Int
    ): T {
        setMaxLength(editText, maxLength)
        editText.text = content as Editable?
        return editText
    }


    /**
     * 设置密码文本视图显示转换
     * @param editText          [EditText]
     * @param isDisplayPassword 是否显示密码
     * @param isSelectBottom    是否设置光标到最后
     * @param <T>               泛型
     * @return [EditText]
    </T> */
    fun <T : EditText> setTransformationMethod(
        editText: T,
        isDisplayPassword: Boolean,
        isSelectBottom: Boolean = true
    ): T {
          // 获取光标位置
            var curSelect = 0
            if (!isSelectBottom) {
                curSelect = editText.selectionStart
            }
            editText.transformationMethod =
                if (isDisplayPassword) HideReturnsTransformationMethod.getInstance() else PasswordTransformationMethod.getInstance()
            if (isSelectBottom) { // 设置光标到最后
                setSelectionToBottom(editText)
            } else { // 设置光标到之前的位置
                setSelection(editText, curSelect)
            }
        return editText
    }


    /**
     * 设置 KeyListener
     * @param editText [EditText]
     * @param accepted 允许输入的内容, 如: 0123456789
     * @param <T>      泛型
     * @return [EditText]
    </T> */
    fun <T : EditText> setKeyListener(editText: T, accepted: String): T {
        editText.keyListener = createDigitsKeyListener(-1, accepted)
        return editText
    }

    /**
     * 获取 DigitsKeyListener ( 限制只能输入字母, 默认弹出英文软键盘 )
     * @return [DigitsKeyListener]
     */
    fun getLettersKeyListener(): DigitsKeyListener {
        return createDigitsKeyListener(InputType.TYPE_TEXT_VARIATION_PASSWORD, LETTERS.toList())
    }

    /**
     * 获取 DigitsKeyListener ( 限制只能输入字母和数字, 默认弹出英文软键盘 )
     * @return [DigitsKeyListener]
     */
    fun getNumberAndLettersKeyListener(): DigitsKeyListener {
        return createDigitsKeyListener(InputType.TYPE_TEXT_VARIATION_PASSWORD, NUMBERS_AND_LETTERS)
    }

    /**
     * 获取 DigitsKeyListener ( 限制只能输入数字, 默认弹出数字列表 )
     * @return [DigitsKeyListener]
     */
    fun getNumberKeyListener(): DigitsKeyListener {
        return createDigitsKeyListener(InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_NORMAL, NUMBERS.toList())
    }

    // =
    /**
     * 创建 DigitsKeyListener
     * @param accepted 允许输入的内容 ( 可以传入 "", 这样无法输入内容 )
     * @return [DigitsKeyListener]
     */
    fun createDigitsKeyListener(accepted: String): DigitsKeyListener {
        return createDigitsKeyListener(-1, accepted.toCharArray())
    }

    /**
     * 创建 DigitsKeyListener
     * @param inputType 输入类型
     * @param accepted  允许输入的内容 ( 可以传入 "", 这样无法输入内容 )
     * @return [DigitsKeyListener]
     */
    fun createDigitsKeyListener(inputType: Int, accepted: String): DigitsKeyListener {
        return createDigitsKeyListener(inputType, accepted.toCharArray())
    }

    // =
    /**
     * 创建 DigitsKeyListener
     * @param accepted 允许输入的内容
     * @return [DigitsKeyListener]
     */
    fun createDigitsKeyListener(accepted: CharArray): DigitsKeyListener {
        return createDigitsKeyListener(-1, accepted)
    }

    fun createDigitsKeyListener(inputType: Int,accepted: List<Char>): DigitsKeyListener {
        return createDigitsKeyListener(inputType, accepted.toCharArray())
    }

    /**
     * 创建 DigitsKeyListener
     * @param inputType 输入类型
     * @param accepted  允许输入的内容
     * @return [DigitsKeyListener]
     */
    fun createDigitsKeyListener(inputType: Int, accepted: CharArray): DigitsKeyListener {
        return object : DigitsKeyListener() {
            override fun getAcceptedChars(): CharArray {
                return accepted
            }

            override fun getInputType(): Int {
                return if (inputType != -1) {
                    inputType
                } else super.getInputType()
            }
        }
    }

    /**
     * Returns the line number of current cursor.
     *
     * @param editText
     * @return
     */
    fun getCurrentCursorLine(editText: EditText): Int {
        val selectionStart = Selection.getSelectionStart(editText.text)
        val layout = editText.layout ?: return -1
        return if (selectionStart != -1) {
            layout.getLineForOffset(selectionStart)
        } else -1
    }

    /**
     * Returns the selected area line numbers.
     *
     * @param editText
     * @return
     */
    fun getCurrentSelectionLines(editText: EditText): IntArray? {
        val editable = editText.text
        val selectionStart = Selection.getSelectionStart(editable)
        val selectionEnd = Selection.getSelectionEnd(editable)
        val layout = editText.layout
        val lines = IntArray(2)
        if (selectionStart != -1) {
            val startLine = layout.getLineForOffset(selectionStart)
            lines[0] = startLine
        }
        if (selectionEnd != -1) {
            val endLine = layout.getLineForOffset(selectionEnd)
            lines[1] = endLine
        }
        return lines
    }

    /**
     * Returns the line start position of the current line (which cursor is focusing now).
     *
     * @param editText
     * @return
     */
    fun getThisLineStart(editText: EditText, currentLine: Int): Int {
        var currentLine = currentLine
        val layout = editText.layout
        var start = 0
        if (currentLine > 0) {
            start = layout.getLineStart(currentLine)
            if (start > 0) {
                val text = editText.text.toString()
                var lastChar = text[start - 1]
                while (lastChar != '\n') {
                    if (currentLine > 0) {
                        currentLine--
                        start = layout.getLineStart(currentLine)
                        lastChar = if (start > 1) {
                            start--
                            text[start]
                        } else {
                            break
                        }
                    }
                }
            }
        }
        return start
    }

    /**
     * Returns the line end position of the current line (which cursor is focusing now).
     *
     * @param editText
     * @return
     */
    fun getThisLineEnd(editText: EditText, currentLine: Int): Int {
        val layout = editText.layout
        return if (-1 != currentLine) {
            layout.getLineEnd(currentLine)
        } else -1
    }


}



@ExperimentalCoroutinesApi
fun EditText.textChangeFlow(): Flow<CharSequence> = callbackFlow {
    val watcher = object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {}

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            s?.let { trySend(it) }
        }
    }
    addTextChangedListener(watcher)
    awaitClose { removeTextChangedListener(watcher) }
}
